Skip to content

fix(editor): reliable unsaved-changes tracking and save feedback - #125

Merged
AdminTeamCoderz merged 2 commits into
mainfrom
fix/editor-dirty-state
Aug 21, 2026
Merged

fix(editor): reliable unsaved-changes tracking and save feedback#125
AdminTeamCoderz merged 2 commits into
mainfrom
fix/editor-dirty-state

Conversation

@AdminTeamCoderz

@AdminTeamCoderz AdminTeamCoderz commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the unreliable unsaved-changes tracking in the editor. Users saw four symptoms,
all stemming from the same state machine:

  • the first keystroke after opening a document did not light the unsaved (orange)
    indicator — only the second did;
  • undoing back to the original content left the indicator stuck on;
  • with the indicator stuck on, the tab's save/close button became unresponsive — it
    stayed in "Save" mode but the save path refused to run, so it neither saved nor closed;
  • after a save, editing back to the originally loaded content wrongly cleared the
    indicator, and the save confirmation toast appeared only sometimes.

Related Issues

None.

Type of Change

Bug fix.

Changes

Four root causes, four targeted changes across two files:

  • packages/editor/src/Editor.tsx — the editor announces its initial content
    checksum via a window event dispatched in its mount effect. Listeners mounted in the
    same React commit attach their effects after the child's, so the announcement was
    lost and the first keystroke's checksum was mistaken for the loaded baseline (wiping
    that keystroke's edit flag — the off-by-one). The dispatch is now deferred by one
    tick so every listener from the same commit is attached first.
  • apps/web/src/components/documents/useDocumentActions.ts — dirty was a one-way
    latch (hasUserEdit): any keystroke set it, only a save cleared it, and nothing
    compared content. Meanwhile the save path used a checksum comparison (isUpToDate).
    The two could disagree — indicator on, save refusing — which is the unresponsive
    button. The tab dirty flag is now hasUserEdit && !isUpToDate: the indicator and the
    save button consume the same condition and cannot deadlock. Undo back to the original
    clears the indicator; redo re-lights it.
  • Same file, instance convergenceuseDocumentActions runs as three independent
    instances per tab (tab button, Cmd+S in edit-document.tsx, tab context menu), each
    with a private baseline; a save only reset the baseline of the instance that performed
    it, so the other two kept pushing a stale opinion of "dirty" into the shared tab state
    (last writer wins — the post-save flakiness). A render-phase adjustment now
    re-baselines any instance whenever the current content is confirmed as the server's
    current head revision (isCurrentHeadContent, derived from the shared query caches),
    so all instances converge after any save. Implemented as render-phase state
    adjustment, matching the file's existing baseline-capture pattern and the repo's
    react-hooks/set-state-in-effect lint rule.
  • Same file, silent saves — when saved content matches a revision the server
    already has, handleUpdate takes a head-move shortcut that produced no feedback at
    all ("the save card doesn't always come up"). Explicit saves on that path now show
    the same "Document saved" toast; autosaves remain silent by design.

How to Test

Manual (no test harness in the repo yet). Run the app, open a document for editing,
and hard-refresh once before starting:

  1. Type one character → the orange indicator appears immediately (was: second
    keystroke).
  2. Undo (Cmd+Z) back to the original → the indicator clears (was: stuck on). Redo →
    it returns.
  3. With the indicator off, the tab button is a normal Close (was: dead Save).
  4. Type, then save — via the tab button, Cmd+S, and the tab context menu — a
    "Document saved" confirmation appears every time, including when the content
    matches an existing revision (was: sometimes silent).
  5. After a save, delete back to the originally loaded text → the indicator stays on
    (that deletion differs from what was just saved) and saving works.
  6. Save again → confirmation, indicator clears.

Expected result: the unsaved indicator always reflects whether the content
actually differs from the last saved state, the save button and the indicator never
contradict each other, and every explicit save gives feedback.

Summary by CodeRabbit

  • Bug Fixes
    • Improved save-state tracking so documents are only marked as changed when edits differ from the saved version.
    • Updated background highlighting to accurately reflect unsaved changes.
    • Added confirmation notifications when explicitly saving content that already matches an existing revision.
    • Improved editor initialization to prevent missed change notifications.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Warning

Your free Security trial is over. An organization admin can activate billing to continue.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 40294265-921b-438a-beff-15685d2c32e1

📥 Commits

Reviewing files that changed from the base of the PR and between c62e83a and 7da7834.

📒 Files selected for processing (2)
  • apps/web/src/components/documents/useDocumentActions.ts
  • packages/editor/src/Editor.tsx

Included review availability: 3 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.


📝 Walkthrough

Walkthrough

The editor now defers initial checksum calculation. Document actions now detect head-matching content, reset local save state, show success notifications for explicit revision reuse, and prevent stale dirty indicators.

Changes

Save state synchronization

Layer / File(s) Summary
Deferred editor checksum initialization
packages/editor/src/Editor.tsx
The initial checksum calculation runs asynchronously through a zero-delay timer. Cleanup cancels the timer.
Document save-state handling
apps/web/src/components/documents/useDocumentActions.ts
Document actions re-baseline state when content matches the document head, show success notifications for explicit revision reuse, and require changed unsaved content for dirty-state indicators.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 7da78

The PR makes localized editor state and save-feedback fixes; no actionable merge-blocking risk remains, so it is merge-ready after normal checks and review.

Suggested reviewers: mohamed-elshesheny

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes to unsaved-changes tracking and save feedback in the editor.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/editor-dirty-state

Comment @coderabbitai help to get the list of available commands.

@AdminTeamCoderz
AdminTeamCoderz merged commit 7ba186e into main Aug 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant